#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>

#import "Texture2D.h"
#import "OpenGL_Internal.h"

#define kMaxTextureSize		1024

@implementation Texture2D

@synthesize contentSize=_size, pixelFormat=_format, pixelsWide=_width, pixelsHigh=_height, name=_name, maxS=_maxS, maxT=_maxT;

- (id) initWithData:(const void*)data pixelFormat:(Texture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size
{
	GLint saveName;
	
	if((self = [super init])) 
	{
		glGenTextures(1, &_name);
		glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName);
		glBindTexture(GL_TEXTURE_2D, _name);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
		switch(pixelFormat) 
		{
			case kTexture2DPixelFormat_RGBA8888:
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
				break;
			
			case kTexture2DPixelFormat_RGBA4444:
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
				break;
			
			case kTexture2DPixelFormat_RGBA5551:
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data);
				break;
			
			case kTexture2DPixelFormat_RGB565:
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
				break;
			
			case kTexture2DPixelFormat_RGB888:
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
				break;
			
			case kTexture2DPixelFormat_L8:
				glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
				break;
			
			case kTexture2DPixelFormat_A8:
				glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
				break;
			
			case kTexture2DPixelFormat_LA88:
				glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
				break;
			
			case kTexture2DPixelFormat_RGB_PVRTC2:
				glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, width, height, 0, (width * height) / 4, data);
				break;
			
			case kTexture2DPixelFormat_RGB_PVRTC4:
				glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, width, height, 0, (width * height) / 2, data);
				break;
			
			case kTexture2DPixelFormat_RGBA_PVRTC2:
				glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, width, height, 0, (width * height) / 4, data);
				break;
			
			case kTexture2DPixelFormat_RGBA_PVRTC4:
				glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, width, height, 0, (width * height) / 2, data);
				break;
			
			default:
				[NSException raise:NSInternalInconsistencyException format:@""];
			
		}
		
		glBindTexture(GL_TEXTURE_2D, saveName);
		
		if(!CHECK_GL_ERROR()) 
		{
			[self release];
			return nil;
		}
		
		_size = size;
		_width = width;
		_height = height;
		_format = pixelFormat;
		_maxS = size.width / (float)width;
		_maxT = size.height / (float)height;
	}
	
	return self;
}

- (void) dealloc
{
	if(_name)
		glDeleteTextures(1, &_name);
	
	[super dealloc];
}

- (NSString*) description
{
	return [NSString stringWithFormat:@"<%@ = %08X | Name = %i | Dimensions = %ix%i | Coordinates = (%.2f, %.2f)>", [self class], self, _name, _width, _height, _maxS, _maxT];
}

@end

@implementation Texture2D (Image)

- (id) initWithImagePath:(NSString*)path
{
	return [self initWithImagePath:path sizeToFit:NO];
}

- (id) initWithImagePath:(NSString*)path sizeToFit:(BOOL)sizeToFit
{
	return [self initWithImagePath:path sizeToFit:sizeToFit pixelFormat:kTexture2DPixelFormat_Automatic];
}

- (id) initWithImagePath:(NSString*)path sizeToFit:(BOOL)sizeToFit pixelFormat:(Texture2DPixelFormat)pixelFormat
{
	UIImage*				uiImage;
	
	if(![path isAbsolutePath])
		path = [[NSBundle mainBundle] pathForResource:path ofType:nil];
	
	uiImage = [[UIImage alloc] initWithContentsOfFile:path];
	self = [self initWithCGImage:[uiImage CGImage] orientation:[uiImage imageOrientation] sizeToFit:sizeToFit pixelFormat:pixelFormat];
	[uiImage release];
	
	if(self == nil)
		REPORT_ERROR(@"Failed loading image at path \"%@\"", path);
	
	return self;
}

- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions fontName:(NSString*)name fontSize:(CGFloat)size drawMode:(int)mode
{
	NSInteger width,height,i;
	CGContextRef context;
	void* data;
	CGColorSpaceRef	colorSpace;
	UIFont * font = [UIFont fontWithName:name size:size];
	
	width = dimensions.width;
	height = dimensions.height;
	
	CGSize txtSize = [string sizeWithFont:font];
	if(width <= 0)
	{
		width = txtSize.width;
		dimensions.width = width;
	}
	if(height <= 0)
	{
		height = txtSize.height;
		dimensions.height = height;
	}
		
	if((width != 1) && (width & (width - 1))) {
		i = 1;
		while(i < width)
			i *= 2;
		width = i;
	}
	
	if((height != 1) && (height & (height - 1))) {
		i = 1;
		while(i < height)
			i *= 2;
		height = i;
	}
	
	colorSpace = CGColorSpaceCreateDeviceGray();
	data = calloc(height, width);
	context = CGBitmapContextCreate(data, width, height, 8, width, colorSpace, kCGImageAlphaNone);
	CGColorSpaceRelease(colorSpace);
	
	
	CGContextSetGrayFillColor(context, 1.0, 1.0);
	CGContextTranslateCTM(context, 0.0, height);
	CGContextScaleCTM(context, 1.0, -1.0); //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
	UIGraphicsPushContext(context);
	[string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withFont:font lineBreakMode:(UILineBreakMode)mode alignment:UITextAlignmentLeft];
	UIGraphicsPopContext();
	
	self = [self initWithData:data pixelFormat:kTexture2DPixelFormat_A8 pixelsWide:width pixelsHigh:height contentSize:dimensions];
	
	CGContextRelease(context);
	free(data);
	
	return self;
}

- (id) initWithCGImage:(CGImageRef)image orientation:(UIImageOrientation)orientation sizeToFit:(BOOL)sizeToFit pixelFormat:(Texture2DPixelFormat)pixelFormat
{
	NSUInteger				width,
							height,
							i;
	CGContextRef			context;
	void*					data;
	CGColorSpaceRef			colorSpace;
	void*					tempData;
	unsigned char*			inPixel8;
	unsigned int*			inPixel32;
	unsigned char*			outPixel8;
	unsigned short*			outPixel16;
	BOOL					hasAlpha;
	CGImageAlphaInfo		info;
	CGAffineTransform		transform;
	CGSize					imageSize;
	
	if(image == NULL)
	{
		[self release];
		return nil;
	}
	
	if(pixelFormat == kTexture2DPixelFormat_Automatic)
	{
		info = CGImageGetAlphaInfo(image);
		hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO);
		
		if(CGImageGetColorSpace(image))
		{
			if(CGColorSpaceGetModel(CGImageGetColorSpace(image)) == kCGColorSpaceModelMonochrome)
			{
				if(hasAlpha)
				{
					pixelFormat = kTexture2DPixelFormat_LA88;
#if __DEBUG__
					if((CGImageGetBitsPerComponent(image) != 8) && (CGImageGetBitsPerPixel(image) != 16))
						REPORT_ERROR(@"Unoptimal image pixel format for image at path \"%@\"", path);
#endif
				}
				else
				{
					pixelFormat = kTexture2DPixelFormat_L8;
#if __DEBUG__
					if((CGImageGetBitsPerComponent(image) != 8) && (CGImageGetBitsPerPixel(image) != 8))
						REPORT_ERROR(@"Unoptimal image pixel format for image at path \"%@\"", path);
#endif
				}
			}
			else
			{
				if((CGImageGetBitsPerPixel(image) == 16) && !hasAlpha)
				{
					pixelFormat = kTexture2DPixelFormat_RGBA5551;
				}
				else
				{
					if(hasAlpha || CGImageGetBitsPerPixel(image) == 32)
					{
						pixelFormat = kTexture2DPixelFormat_RGBA8888;
					}
					else 
					{
						pixelFormat = kTexture2DPixelFormat_RGB565;
#if __DEBUG__
						if((CGImageGetBitsPerComponent(image) != 8) && (CGImageGetBitsPerPixel(image) != 24))
							REPORT_ERROR(@"Unoptimal image pixel format for image at path \"%s\"", path);
#endif
					}
				}
			}		
		}
		else 
		{ //NOTE: No colorspace means a mask image
			pixelFormat = kTexture2DPixelFormat_A8;
#if __DEBUG__
			if((CGImageGetBitsPerComponent(image) != 8) && (CGImageGetBitsPerPixel(image) != 8))
				REPORT_ERROR(@"Unoptimal image pixel format for image at path \"%@\"", path);
#endif
		}
	}
	
	imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
	
	switch(orientation)
	{
		case UIImageOrientationUp: //EXIF = 1
			transform = CGAffineTransformIdentity;
			break;
		
		case UIImageOrientationUpMirrored: //EXIF = 2
			transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
			transform = CGAffineTransformScale(transform, -1.0, 1.0);
			break;
		
		case UIImageOrientationDown: //EXIF = 3
			transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
			transform = CGAffineTransformRotate(transform, M_PI);
			break;
		
		case UIImageOrientationDownMirrored: //EXIF = 4
			transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
			transform = CGAffineTransformScale(transform, 1.0, -1.0);
			break;
		
		case UIImageOrientationLeftMirrored: //EXIF = 5
			transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
			transform = CGAffineTransformScale(transform, -1.0, 1.0);
			transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
			break;
		
		case UIImageOrientationLeft: //EXIF = 6
			transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
			transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
			break;
		
		case UIImageOrientationRightMirrored: //EXIF = 7
			transform = CGAffineTransformMakeScale(-1.0, 1.0);
			transform = CGAffineTransformRotate(transform, M_PI / 2.0);
			break;
		
		case UIImageOrientationRight: //EXIF = 8
			transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
			transform = CGAffineTransformRotate(transform, M_PI / 2.0);
			break;
		
		default:
			[NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];
		
	}
	
	if((orientation == UIImageOrientationLeftMirrored) || (orientation == UIImageOrientationLeft) || (orientation == UIImageOrientationRightMirrored) || (orientation == UIImageOrientationRight))
		imageSize = CGSizeMake(imageSize.height, imageSize.width);
	
	width = imageSize.width;
	if((width != 1) && (width & (width - 1))) 
	{
		i = 1;
		while((sizeToFit ? 2 * i : i) < width)
		i *= 2;
		width = i;
	}
	
	height = imageSize.height;
	if((height != 1) && (height & (height - 1))) 
	{
		i = 1;
		while((sizeToFit ? 2 * i : i) < height)
		i *= 2;
		height = i;
	}
	
	while((width > kMaxTextureSize) || (height > kMaxTextureSize)) 
	{
#if __DEBUG__
		REPORT_ERROR(@"Image at %ix%i pixels is too big to fit in texture", width, height);
#endif
		width /= 2;
		height /= 2;
		transform = CGAffineTransformScale(transform, 0.5, 0.5);
		imageSize.width *= 0.5;
		imageSize.height *= 0.5;
	}
	
	switch(pixelFormat)
	{
		case kTexture2DPixelFormat_RGBA8888:
		case kTexture2DPixelFormat_RGBA4444:
			colorSpace = CGColorSpaceCreateDeviceRGB();
			data = malloc(height * width * 4);
			context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
			CGColorSpaceRelease(colorSpace);
			break;
		
		case kTexture2DPixelFormat_RGBA5551:
			colorSpace = CGColorSpaceCreateDeviceRGB();
			data = malloc(height * width * 2);
			context = CGBitmapContextCreate(data, width, height, 5, 2 * width, colorSpace, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder16Little);
			CGColorSpaceRelease(colorSpace);
			break;
		
		case kTexture2DPixelFormat_RGB888:
		case kTexture2DPixelFormat_RGB565:
			colorSpace = CGColorSpaceCreateDeviceRGB();
			data = malloc(height * width * 4);
			context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big );
			CGColorSpaceRelease(colorSpace);
			break;
		
		case kTexture2DPixelFormat_L8:
			colorSpace = CGColorSpaceCreateDeviceGray();
			data = malloc(height * width);
			context = CGBitmapContextCreate(data, width, height, 8, width, colorSpace, kCGImageAlphaNone);
			CGColorSpaceRelease(colorSpace);
			break;
		
		case kTexture2DPixelFormat_A8:
			data = malloc(height * width);
			context = CGBitmapContextCreate(data, width, height, 8, width, NULL, kCGImageAlphaOnly);
			break;
		
		case kTexture2DPixelFormat_LA88:
			colorSpace = CGColorSpaceCreateDeviceRGB();
			data = malloc(height * width * 4);
			context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
			CGColorSpaceRelease(colorSpace);
			break;
		
		default:
			[NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"];
		
	}
	
	if(context == NULL) 
	{
		REPORT_ERROR(@"Failed creating CGBitmapContext", NULL);
		free(data);
		[self release];
		return nil;
	}
	
	if(sizeToFit)
	{
		CGContextScaleCTM(context, (CGFloat)width / imageSize.width, (CGFloat)height / imageSize.height);
	}
	else
	{
		CGContextClearRect(context, CGRectMake(0, 0, width, height));
		CGContextTranslateCTM(context, 0, height - imageSize.height);
	}
	
	if(!CGAffineTransformIsIdentity(transform))
		CGContextConcatCTM(context, transform);
	
	CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
	
	//Convert "-RRRRRGGGGGBBBBB" to "RRRRRGGGGGBBBBBA"
	if(pixelFormat == kTexture2DPixelFormat_RGBA5551)
	{
		outPixel16 = (unsigned short*)data;
		for(i = 0; i < width * height; ++i, ++outPixel16)
		*outPixel16 = *outPixel16 << 1 | 0x0001;
#if __DEBUG__
		REPORT_ERROR(@"Falling off fast-path converting pixel data from ARGB1555 to RGBA5551", NULL);
#endif
	}
	//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRRRRRGGGGGGGGBBBBBBBB"
	else if(pixelFormat == kTexture2DPixelFormat_RGB888)
	{
		tempData = malloc(height * width * 3);
		inPixel8 = (unsigned char*)data;
		outPixel8 = (unsigned char*)tempData;
		for(i = 0; i < width * height; ++i)
		{
			*outPixel8++ = *inPixel8++;
			*outPixel8++ = *inPixel8++;
			*outPixel8++ = *inPixel8++;
			inPixel8++;
		}
		free(data);
		data = tempData;
#if __DEBUG__
		REPORT_ERROR(@"Falling off fast-path converting pixel data from RGBA8888 to RGB888", NULL);
#endif
	}
	//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
	else if(pixelFormat == kTexture2DPixelFormat_RGB565)
	{
		tempData = malloc(height * width * 2);
		inPixel32 = (unsigned int*)data;
		outPixel16 = (unsigned short*)tempData;
		for(i = 0; i < width * height; ++i, ++inPixel32)
		*outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);
		free(data);
		data = tempData;
#if __DEBUG__
		REPORT_ERROR(@"Falling off fast-path converting pixel data from RGBA8888 to RGB565", NULL);
#endif
	}
	//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGBBBBAAAA"
	else if(pixelFormat == kTexture2DPixelFormat_RGBA4444) 
	{
		tempData = malloc(height * width * 2);
		inPixel32 = (unsigned int*)data;
		outPixel16 = (unsigned short*)tempData;
		for(i = 0; i < width * height; ++i, ++inPixel32)
		*outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | ((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0);
		free(data);
		data = tempData;
#if __DEBUG__
		REPORT_ERROR(@"Falling off fast-path converting pixel data from RGBA8888 to RGBA4444", NULL);
#endif
	}
	//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "LLLLLLLLAAAAAAAA"
	else if(pixelFormat == kTexture2DPixelFormat_LA88) 
	{
		tempData = malloc(height * width * 3);
		inPixel8 = (unsigned char*)data;
		outPixel8 = (unsigned char*)tempData;
		for(i = 0; i < width * height; ++i) {
			*outPixel8++ = *inPixel8++;
			inPixel8 += 2;
			*outPixel8++ = *inPixel8++;
		}
		free(data);
		data = tempData;
#if __DEBUG__
		REPORT_ERROR(@"Falling off fast-path converting pixel data from RGBA8888 to LA88", NULL);
#endif
	}
	
	self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:width pixelsHigh:height contentSize:imageSize];
	
	CGContextRelease(context);
	free(data);
	
	return self;
}

@end

@implementation Texture2D (Drawing)

- (void) preload
{
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	
	[self drawInRect:CGRectMake(-2, -2, 0.1, 0.1)];
	
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}

- (void) drawAtVertices:(CGFloat*)vertices
{
	GLfloat				coordinates[] = {
		0,				0,
		_maxS,			0,
		0,				_maxT,
		_maxS,			_maxT
	};
	
	glBindTexture(GL_TEXTURE_2D, _name);
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

- (void) drawAtPoint2D:(CGPoint)point rotate:(int)rot scalex:(CGFloat)scalex scaley:(CGFloat)scaley region:(CGRect)region;
{
	CGFloat depth = 0.0;
	GLfloat width  = _size.width;
	GLfloat height = _size.height;
	GLfloat x = 0,y = 0,dx = _maxS,dy = _maxT;
	if(region.size.width > 0 && region.size.height > 0)
	{
		x = (float)region.origin.x / (float)_size.width  * _maxS;
		y = (float)region.origin.y / (float)_size.height * _maxT;
		dx = (float)( region.origin.x + region.size.width ) / (float)_size.width   * _maxS;
		dy = (float)( region.origin.y + region.size.height ) / (float)_size.height * _maxT;
		
		width  = region.size.width;
		height = region.size.height;
	}
	if(region.size.width < 0)
	{
		x = dx;
		dx = 0;
	}
	if(region.size.height < 0)
	{
		y = dy;
		dy = 0;
	}
	GLfloat	coordinates[] = { x, y, dx, y, x, dy, dx, dy };
	GLfloat	vertices[] = {
				point.x,		point.y,		depth,
				point.x + width,		point.y,		depth,
				point.x,		point.y + height,		depth,
				point.x + width,		point.y + height,		depth
			};

	glPushMatrix();
	if(rot!=0 || scalex != 1 || scaley != 1)
	{
		GLfloat centerx = point.x + width/2;
		GLfloat centery = point.y + height/2;
		glTranslatef(centerx , centery, depth);
		glRotatef(rot, 0, 0, 1);
		glScalef(scalex, scaley, 1);
		glTranslatef(-centerx, -centery, -depth);
	}
	glBindTexture(GL_TEXTURE_2D, _name);
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	glPopMatrix();
}

- (void) drawAtPoint3D:(CGPoint)point depth:(CGFloat)depth rotatex:(int)rotx rotatey:(int)roty rotatez:(int)rotz scale:(CGFloat)scale region:(CGRect)region
{
	GLfloat width  = _size.width;
	GLfloat height = _size.height;
	GLfloat x = 0,y = 0,dx = _maxS,dy = _maxT;
	if(region.size.width > 0 && region.size.height > 0)
	{
		x = (float)region.origin.x / (float)_size.width  * _maxS;
		y = (float)region.origin.y / (float)_size.height * _maxT;
		dx = (float)( region.origin.x + region.size.width ) / (float)_size.width   * _maxS;
		dy = (float)( region.origin.y + region.size.height ) / (float)_size.height * _maxT;
		
		width  = region.size.width;
		height = region.size.height;
	}
	if(depth == 0xFFFFFFFF)
	{
		point.x -= 160;
		point.y = -point.y + 240;
		depth = -400;
	}
	height = -height;
	GLfloat	coordinates[] = { x, y, dx, y, x, dy, dx, dy };
	GLfloat	vertices[] = {
		point.x,		point.y,		depth,
		point.x + width,		point.y,		depth,
		point.x,		point.y + height,		depth,
		point.x + width,		point.y + height,		depth
	};
		
	glPushMatrix();
	if(rotx!=0 || roty!=0 || rotz!=0 || scale!=1)
	{
		GLfloat centerx = point.x + width/2;
		GLfloat centery = point.y + height/2;
		glTranslatef(centerx , centery, depth);
		glRotatef(rotx, 1, 0, 0);
		glRotatef(roty, 0, 1, 0);
		glRotatef(rotz, 0, 0, 1);
		glScalef(scale, scale, scale);
		glTranslatef(-centerx , -centery, -depth);
	}
	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glBindTexture(GL_TEXTURE_2D, _name);
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	//glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
	glPopMatrix();
}
	
- (void) drawInRect:(CGRect)rect
{
	[self drawInRect:rect depth:0.0];
}

- (void) blit:(CGRect)rect region:(CGRect)region transform:(int)transform
{
	float x = (float)region.origin.x / (float)_size.width;
	float y = (float)region.origin.y / (float)_size.height;
	float dx = (float)( region.origin.x + region.size.width ) / (float)_size.width;
	float dy = (float)( region.origin.y + region.size.height ) / (float)_size.height;
	
	x *= _maxS;
	y *= _maxT;
	dx *= _maxS;
	dy *= _maxT;
	
	if( transform == 1 )
	{
		float temp = x;
		x = dx;
		dx = temp;
	}
	else if( transform == 2 )
	{
		float temp = y;
		y = dy;
		dy = temp;
	}
	
	float depth = 0.0;
	
	GLfloat				coordinates[] = { x, y, dx, y, x, dy, dx, dy };
	GLfloat				vertices[] = {
		rect.origin.x,							rect.origin.y,							depth,
		rect.origin.x + rect.size.width,		rect.origin.y,							depth,
		rect.origin.x,							rect.origin.y + rect.size.height,		depth,
		rect.origin.x + rect.size.width,		rect.origin.y + rect.size.height,		depth
	};
	
	glBindTexture(GL_TEXTURE_2D, _name);
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

- (void) blit:(CGRect)rect region:(CGRect)region transform:(int)transform angle:(float)angle
{
	float x = (float)region.origin.x / (float)_size.width;
	float y = (float)region.origin.y / (float)_size.height;
	float dx = (float)( region.origin.x + region.size.width ) / (float)_size.width;
	float dy = (float)( region.origin.y + region.size.height ) / (float)_size.height;
	
	x *= _maxS;
	y *= _maxT;
	dx *= _maxS;
	dy *= _maxT;
	
	if( transform == 1 )
	{
		float temp = x;
		x = dx;
		dx = temp;
	}
	else if( transform == 2 )
	{
		float temp = y;
		y = dy;
		dy = temp;
	}
	
	float depth = 0.0;
	float halfWidth = rect.size.width / 2.0f;
	float halfHeight = rect.size.height / 2.0f;
	
	GLfloat coordinates[] = { x, y, dx, y, x, dy, dx, dy };
	GLfloat vertices[] = {
		-halfWidth, -halfHeight, depth,
		halfWidth,	-halfHeight, depth,
		-halfWidth, halfHeight, depth,
		halfWidth, halfHeight, depth
	};
	
	glPushMatrix();
	
	glTranslatef( rect.origin.x + halfWidth, rect.origin.y + halfHeight, 0);
	
	if( angle )
		glRotatef(-angle, 0, 0, 1);

	glBindTexture(GL_TEXTURE_2D, _name);
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	
	glPopMatrix();
}

- (void) blit:(CGRect)rect region:(CGRect)region transform:(int)transform angle:(float)angle alpha:(float)alpha
{
	float x = (float)region.origin.x / (float)_size.width;
	float y = (float)region.origin.y / (float)_size.height;
	float dx = (float)( region.origin.x + region.size.width ) / (float)_size.width;
	float dy = (float)( region.origin.y + region.size.height ) / (float)_size.height;
	
	x *= _maxS;
	y *= _maxT;
	dx *= _maxS;
	dy *= _maxT;
	
	if( transform == 1 )
	{
		float temp = x;
		x = dx;
		dx = temp;
	}
	else if( transform == 2 )
	{
		float temp = y;
		y = dy;
		dy = temp;
	}
	
	float depth = 0.0;
	float halfWidth = rect.size.width / 2.0f;
	float halfHeight = rect.size.height / 2.0f;
	
	GLfloat coordinates[] = { x, y, dx, y, x, dy, dx, dy };
	GLfloat vertices[] = {
		-halfWidth, -halfHeight, depth,
		halfWidth,	-halfHeight, depth,
		-halfWidth, halfHeight, depth,
		halfWidth, halfHeight, depth
	};
	
	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	
	glPushMatrix();
	glTranslatef( rect.origin.x + halfWidth, rect.origin.y + halfHeight, 0);
	if( angle )
		glRotatef(-angle, 0, 0, 1);
	
	glBindTexture(GL_TEXTURE_2D, _name);
	
	glColor4f(1, 1, 1, alpha);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	
	glColor4f(1, 1, 1, 1);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	
	glPopMatrix();
	
	//glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}

- (void) drawInRect:(CGRect)rect depth:(CGFloat)depth
{
	float x = 0;
	float y = 0;
	float dx = _maxS;
	float dy = _maxT;
	
	GLfloat				coordinates[] = { x, y, dx, y, x, dy, dx, dy };
	GLfloat				vertices[] = {
							rect.origin.x,							rect.origin.y,							depth,
							rect.origin.x + rect.size.width,		rect.origin.y,							depth,
							rect.origin.x,							rect.origin.y + rect.size.height,		depth,
							rect.origin.x + rect.size.width,		rect.origin.y + rect.size.height,		depth
						};
	
	glBindTexture(GL_TEXTURE_2D, _name);
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

- (void) drawSphereAtPoint2D:(CGPoint)point rollAngle:(int)rollAngle rotateAngle:(int)rotateAngle
{
	int angleIndex = (rollAngle % 360 / 6);
	if (angleIndex < 0) angleIndex += 60;
	int i = angleIndex / 10;
	int j = angleIndex % 10;
	[self blit:CGRectMake(point.x - 12, point.y - 12, 24, 24) region:CGRectMake(j * 24, i * 24, 24, 24) transform:0 angle:-rotateAngle];
}

static GLfloat ball_vertices[] = 
{
/*
-5.705405, 8.06866, -5.705405, 
-11.410809, -.000001, .000001, 
-8.068662, 8.06866, 0, 
-8.06866, 0, 8.068662, 
-5.705404, 8.06866, 5.705405, 
.000002, 0, 11.410809, 
.000001, 8.06866, 8.06866, 
8.068662, 0, 8.06866, 
5.705406, 8.06866, 5.705403, 
11.410809, 0, -.000001, 
8.06866, 8.06866, -.000001, 
8.068661, 0, -8.068661, 
5.705404, 8.06866, -5.705406, 
-.000001, -.000001, -11.410809, 
-.000001, 8.06866, -8.068662, 
-8.068661, -.000001, -8.068659, 
5.705404, -8.068661, -5.705405, 
11.410809, 0, -.000001, 
8.06866, -8.068661, -.000001, 
8.068662, 0, 8.06866, 
5.705406, -8.068661, 5.705402, 
.000002, 0, 11.410809, 
.000002, -8.068661, 8.06866, 
-8.06866, 0, 8.068662, 
-5.705403, -8.068661, 5.705404, 
-11.410809, -.000001, .000001, 
-8.06866, -8.068661, .000001, 
-8.068661, -.000001, -8.068659, 
-5.705404, -8.068661, -5.705404, 
-.000001, -.000001, -11.410809, 
0, -8.068661, -8.06866, 
8.068661, 0, -8.068661, 
0, 11.410809, -.000001, 
.000001, -11.410809, 0,
*/
-4.124494, -2.89737, 10.43598,
1.010127, -.000001, 11.545383,
-4.897888, 0, 10.503643,
-4.124495, 2.897368, 10.43598,
-2.011543, 5.018391, 10.25112,
.8748, 5.794739, 9.998597,
3.761145, 5.018392, 9.746076,
5.874096, 2.89737, 9.561216,
6.647489, 0, 9.493553,
5.874096, -2.89737, 9.561216,
3.761146, -5.018392, 9.746075,
.874801, -5.79474, 9.998597,
-2.011544, -5.018392, 10.25112,
-6.647413, 0, -9.493538,
-1.010051, .000001, -11.545371,
-5.874019, -2.897369, -9.561201,
-3.761069, -5.01839, -9.74606,
-.874724, -5.794738, -9.998584,
2.01162, -5.01839, -10.251104,
4.124571, -2.897369, -10.435965,
4.897964, .000001, -10.503628,
4.12457, 2.897371, -10.435965,
2.011619, 5.018392, -10.251106,
-.874726, 5.79474, -9.998584,
-3.76107, 5.018392, -9.74606,
-5.87402, 2.897369, -9.561201,
-8.153952, -5.018392, 6.530262,
-4.494213, -8.692108, 6.210076,
.505083, -10.036783, 5.772695,
5.504378, -8.692109, 5.335314,
9.164115, -5.018391, 5.015129,
10.503672, .000001, 4.897933,
9.164115, 5.018393, 5.015129,
5.504376, 8.69211, 5.335314,
.505081, 10.036784, 5.772696,
-4.494214, 8.692109, 6.210077,
-8.153954, 5.01839, 6.530262,
-9.49351, 0, 6.647459,
-9.998554, -5.79474, .874768,
-5.772651, -10.036781, .50505,
.000038, -11.589478, .000006,
5.772728, -10.036781, -.505038,
9.998627, -5.794739, -.874757,
11.545413, .000001, -1.010082,
9.998626, 5.794741, -.874755,
5.772726, 10.036785, -.505037,
.000036, 11.589481, .000007,
-5.772653, 10.036783, .505052,
-9.998555, 5.794738, .87477,
-11.545343, 0, 1.010095,
-11.545343, 0, 1.010095,
-9.164041, -5.018391, -5.015116,
-9.998554, -5.79474, .874768,
-5.504301, -8.692107, -5.335301,
-5.772651, -10.036781, .50505,
-.505006, -10.03678, -5.772682,
.000038, -11.589478, .000006,
4.494289, -8.692107, -6.210064,
5.772728, -10.036781, -.505038,
8.154025, -5.01839, -6.530249,
9.998627, -5.794739, -.874757,
9.493582, .000001, -6.647443,
11.545413, .000001, -1.010082,
8.154024, 5.018393, -6.530248,
9.998626, 5.794741, -.874755,
4.494288, 8.692111, -6.210063,
5.772726, 10.036785, -.505037,
-.505008, 10.036783, -5.772681,
.000036, 11.589481, .000007,
-5.504302, 8.692108, -5.3353,
-5.772653, 10.036783, .505052,
-9.164042, 5.018391, -5.015115,
-9.998555, 5.794738, .87477,
-10.503599, 0, -4.897919,
};

static GLfloat ball_tex_coords[] = 
{
/*
.69907, .69907,
.5, .991728,
.5, .781527,
.062966, .927244,
.30093, .69907,
.065296, .557269,
.218473, .5,
.066638, .152296,
.30093, .30093,
.5, .008272,
.5, .218473,
.966647, .133206,
.69907, .30093,
.953549, .430005,
.781527, .5,
.952697, .728683,
.5, .008272,
.30093, .30093,
.066638, .152296,
.218473, .5,
.065296, .557269,
.30093, .69907,
.062967, .927244,
.5, .781527,
.5, .991728,
.69907, .69907,
.952697, .728682,
.781527, .5,
.953549, .430005,
.699069, .30093,
.966647, .133206,
.5, .218473,
.5, .5,
.5, .5,
*/
.577669, .365473,
.5, .5,
.5, .344662,
.422331, .365473,
.365473, .422331,
.344662, .5,
.365473, .577669,
.422331, .634527,
.5, .655338,
.577669, .634527,
.634527, .577669,
.655338, .5,
.634527, .422331,
.5, .344662,
.5, .5,
.422331, .365473,
.365473, .422331,
.344662, .5,
.365473, .577669,
.422331, .634527,
.5, .655338,
.577669, .634527,
.634527, .577669,
.655338, .5,
.634527, .422331,
.577669, .365473,
.656281, .229313,
.770687, .343719,
.805364, .5,
.770687, .656281,
.656281, .770687,
.5, .812562,
.343719, .770687,
.229313, .656281,
.187438, .5,
.229313, .343719,
.343719, .229313,
.5, .187438,
.740522, .083403,
.896682, .237643,
.981045, .5,
.979351, .740522,
.740522, .916597,
.5, .981045,
.24988, .947789,
.01923, .786594,
.018955, .511997,
.018619, .365052,
.259478, .083403,
.5, .018955,
.5, .018955,
.343719, .229313,
.259478, .083403,
.229313, .343719,
.018619, .365052,
.187438, .5,
.018955, .511997,
.229313, .656281,
.019229, .786594,
.343719, .770687,
.24988, .947789,
.5, .812562,
.5, .981045,
.656281, .770687,
.740523, .916597,
.770687, .656281,
.979351, .740522,
.805364, .5,
.981045, .5,
.770687, .343719,
.896682, .237643,
.656281, .229313,
.740522, .083403,
.5, .187438,
};


static GLubyte ball_strip0[] = { 0, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, 1, 10, 1, 11, 1, 12, 1, 0 };

static GLubyte ball_strip1[] = { 13, 14, 15, 14, 16, 14, 17, 14, 18, 14, 19, 14, 20, 14, 21, 14, 22, 14, 23, 14, 24, 14, 25, 14, 13 };

static GLubyte ball_strip2[] = { 2, 26, 0, 27, 12, 28, 11, 29, 10, 30, 9, 31, 8, 32, 7, 33, 6, 34, 5, 35, 4, 36, 3, 37, 2, 26 };

static GLubyte ball_strip3[] = { 37, 38, 26, 39, 27, 40, 28, 41, 29, 42, 30, 43, 31, 44, 32, 45, 33, 46, 34, 47, 35, 48, 36, 49, 37, 38 };

static GLubyte ball_strip4[] = { 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 50, 51 };

static GLubyte ball_strip5[] = { 73, 15, 51, 16, 53, 17, 55, 18, 57, 19, 59, 20, 61, 21, 63, 22, 65, 23, 67, 24, 69, 25, 71, 13, 73, 15 };

 
/*
static GLubyte ball_strip0[] = {  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1 };

static GLubyte ball_strip1[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17 };

static GLubyte ball_strip2[] = { 8, 10, 32, 12, 14 };

static GLubyte ball_strip3[] = { 14, 0, 32, 2, 4 };

static GLubyte ball_strip4[] = { 19, 17, 33, 31, 29 };

static GLubyte ball_strip5[] = { 25, 23, 33, 21, 19 };

static GLubyte ball_strip6[] = { 4, 6, 32, 8 };

static GLubyte ball_strip7[] = { 25, 33, 27, 29 };
*/

- (void) drawSphereAtPoint2:(CGPoint)point rollAngle:(int)rollAngle rotateAngle:(int)rotateAngle
{
	glPushMatrix();
	glLoadIdentity();
	
	glTranslatef(point.x, point.y, 0);
	glRotatef(rotateAngle, 0, 0, 1);
	glRotatef(rollAngle, 0, 1, 0);
	
	//glScalef(0.0004, 0.0004, 0.0004);
	
	
	 glDrawElements(GL_TRIANGLE_STRIP, 25, GL_UNSIGNED_BYTE, ball_strip0);
	 glDrawElements(GL_TRIANGLE_STRIP, 25, GL_UNSIGNED_BYTE, ball_strip1);
	 glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_BYTE, ball_strip2);
	 glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_BYTE, ball_strip3);
	 glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_BYTE, ball_strip4);
	 glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_BYTE, ball_strip5);
	
	/*
	 glDrawElements(GL_TRIANGLE_STRIP, 18, GL_UNSIGNED_BYTE, ball_strip0);
	 glDrawElements(GL_TRIANGLE_STRIP, 18, GL_UNSIGNED_BYTE, ball_strip1);
	 glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_BYTE, ball_strip2);
	 glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_BYTE, ball_strip3);
	 glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_BYTE, ball_strip4);
	 glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_BYTE, ball_strip5);
	 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, ball_strip6);
	 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, ball_strip7);
	 */
	
	glPopMatrix();
}

- (void) drawSphereAtPoint:(CGPoint)point rollAngle:(int)rollAngle rotateAngle:(int)rotateAngle
{
	//glEnable(GL_DEPTH_TEST);
	//glClear(GL_DEPTH_BUFFER_BIT);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	
	glBindTexture(GL_TEXTURE_2D, _name);	
	glTexCoordPointer(2, GL_FLOAT, 0, ball_tex_coords);
	//glTexCoordPointer(2, GL_SHORT, 0, ball_tex_coords_short);
	glVertexPointer(3, GL_FLOAT, 0, ball_vertices);
	//glVertexPointer(3, GL_SHORT, 0, ball_vertices_short);
	
	glPushMatrix();
	glLoadIdentity();
	
	glTranslatef(point.x, point.y, 0);
	glRotatef(rotateAngle, 0, 0, 1);
	glRotatef(rollAngle, 0, 1, 0);

	//glScalef(0.0004, 0.0004, 0.0004);
	
	
	glDrawElements(GL_TRIANGLE_STRIP, 25, GL_UNSIGNED_BYTE, ball_strip0);
	glDrawElements(GL_TRIANGLE_STRIP, 25, GL_UNSIGNED_BYTE, ball_strip1);
	glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_BYTE, ball_strip2);
	glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_BYTE, ball_strip3);
	glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_BYTE, ball_strip4);
	glDrawElements(GL_TRIANGLE_STRIP, 26, GL_UNSIGNED_BYTE, ball_strip5);
	
	/*
	glDrawElements(GL_TRIANGLE_STRIP, 18, GL_UNSIGNED_BYTE, ball_strip0);
	glDrawElements(GL_TRIANGLE_STRIP, 18, GL_UNSIGNED_BYTE, ball_strip1);
	glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_BYTE, ball_strip2);
	glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_BYTE, ball_strip3);
	glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_BYTE, ball_strip4);
	glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_BYTE, ball_strip5);
	glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, ball_strip6);
	glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, ball_strip7);
	*/
	
	glPopMatrix();
	
	//glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
}

@end
